/**
Company: Sarkar software technologys
WebSite: http://www.sarkar.cn
Author: yeganaaa
Date : 5/22/18
Time: 5:41 AM
 */

package Cn.Sarkar.PermissionManager

import Cn.Sarkar.PermissionManager.Entities.IPermissionItem
import io.ktor.application.ApplicationCallPipeline
import io.ktor.application.ApplicationFeature
import io.ktor.http.HttpHeaders
import io.ktor.util.AttributeKey
import kotlinx.coroutines.experimental.async


class PermissionManager(val configuration: Configuration, val permissions: Array<IPermissionItem>) {

    data class Configuration(
            var expireSecondOfNonces: Int = 60 * 5,
            var scanTimeSecondOfNonces: Int = 60 * 5,
            private var _lastScannedTime: Long = System.currentTimeMillis(),
            val permissions: ArrayList<IPermissionItem> = ArrayList()
    ) {
        val lastScannedTime: Long get() = _lastScannedTime
        internal fun runIfItIsTimeToClean(action: suspend () -> Unit) {
            synchronized(this) {
                if (System.currentTimeMillis() > lastScannedTime + (scanTimeSecondOfNonces * 1000)) {
                    _lastScannedTime = System.currentTimeMillis()
                    async { action() }
                }
            }
        }

        internal fun isExpiredNonce(nonceValue: Long) = nonceValue + (expireSecondOfNonces * 1000) < System.currentTimeMillis()

    }

    val nonces = HashMap<String, Long>(100)


    companion object : ApplicationFeature<ApplicationCallPipeline, Configuration, PermissionManager> {
        override val key: AttributeKey<PermissionManager> by lazy { AttributeKey<PermissionManager>("Cn.Sarkar.PermissionManager") }

        override fun install(pipeline: ApplicationCallPipeline, configure: Configuration.() -> Unit): PermissionManager {
            val config = Configuration().apply(configure)
            return PermissionManager(config, config.permissions.toTypedArray()).apply {
                install(pipeline)
            }
        }
    }

    private fun install(pipeline: ApplicationCallPipeline) {

        pipeline.intercept(ApplicationCallPipeline.Infrastructure) {
            configuration.runIfItIsTimeToClean {

                nonces.toList().forEach {
                    if (configuration.isExpiredNonce(it.second)) {
                        nonces.remove(it.first)
                    }
                }
            }
        }

        pipeline.intercept(ApplicationCallPipeline.Fallback) {
            val hs = context.response.headers.allValues()
            val header = context.response.headers[HttpHeaders.WWWAuthenticate]
            if (header != null) {
                header.split(",").map { it.trim() }.filter { it != "" && it.startsWith("nonce=") }.first().apply {
                    val retvNonce = this.replace("nonce=\"", "").replace("\"", "")

                    nonces.put(retvNonce, System.currentTimeMillis())
                }
            }
        }
    }

    fun existsNonce(nonce: String): Boolean = nonces[nonce].run {
        if (this == null)
            false
        else
            configuration.isExpiredNonce(this).also { nonces.remove(nonce) }
    }
}
